When formulating a model for larger companies, you frequently encounter models that are not limited to a single plant in order to produce the product. Therefore, in this session, you will create a production planning model that includes multiple plants. You will take the model from the previous session and upgrade it to include another index, plants, which will represent all of the plants that are available in order to produce the products. You will then go through the model, step by step, and update all the variable vectors and constraints to account for the new index.
Location indexes are quite common when formulating production planning models. One example of a location index, would be to represent the plants where the company produces the products for the company. Other examples would include warehouses, factories, distribution centers, etc.
It is common, when working with models that include location indexes, that shipping is allowed between the locations. These models are often called transportation or distribution models and will be covered in later sessions.
When formulating small simple models it is reasonable to leave the data definitions inside the model. As soon as you start working with multi-dimensional models, this becomes difficult to manage, and it is necessary to move the data into separate data files. Keeping the data separate from the model, enhances the readability of the model and make the data easier to maintain.
The model you are creating in this session has multiple indexes, product, month, and plant, and data vectors such as Demand, that are two-dimensional and could be moved into a separate data file. In the model, instead of listing all the data elements for the data vector, you can use the keyword DATAFILE and then the name of the data file as shown here below:
demand[product, month] := DATAFILE("Demand.dat");
In this session, you will create a new model formulation for the production planning model to include multiple plants, as well as the multiple periods that were introduced in Session 4. What you want to decide is how much to produce of each product, for each month, in each plant, as well as how much to sell and store in inventory, for each month, in each plant.
In this new problem you are going to have four different plants p1, p2, p3, and p4. Any of these plants can produce all three of the products. Create an index called plants that contains the four different plants, and then update the model accordingly by adding the index to the applicable vectors.
As in the previous session, the selling price stays the same for each product $120.00, $100.00, and $150.00, respectively. The product demands also remain the same as in the previos session. Refer to the demand table in Session 3 for the necessary data.
Now that you have multiple plants the production cost for each product is different for each plant. This data is in the table below.
Production Cost | A1 | A2 | A3 |
---|---|---|---|
plant 1 | $73.30 | $52.90 | $65.40 |
plant 2 | $79.00 | $52.00 | $66.80 |
plant 3 | $75.80 | $52.10 | $50.90 |
plant 4 | $82.70 | $63.30 | $53.80 |
The production rate for each product is also different for each plant as shown in the table below:
Production Rate | A1 | A2 | A3 |
---|---|---|---|
plant 1 | 500 | 450 | 450 |
plant 2 | 550 | 450 | 300 |
plant 3 | 450 | 350 | 300 |
plant 4 | 550 | 400 | 350 |
Listed below is the entire model formulation for Planning5. The additions to the model are highlighted in boldface in order to make it easy for you to see the changes from the model in Session 4.
TITLE Production_Planning5; INDEX product := (A1, A2, A3); month := (Jan, Feb, Mar, Apr); plant := (p1, p2, p3, p4); DATA Price[product] := (120.00, 100.00, 115.00); Demand[product, month] := DATAFILE("Demand.dat"); ProdCost[plant, product] := DATAFILE("ProdCost.dat"); ProdRate[plant, product] := DATAFILE("ProdRate.dat"); ProdDaysAvail[month] := (23, 20, 23, 22); InvtCost[product] := (3.50, 4.00, 3.00); InvtCapacity := 800; VARIABLES Produce[plant, product, month] -> Prod; Inventory[product, month] -> Invt; Sales[product, month] -> Sale; MACROS TotalRevenue := SUM(product, month: Price * Sales); TotalProdCost := SUM(plant, product, month: ProdCost * Produce); TotalInvtCost := SUM(product, month: InvtCost * Inventory); TotalCost := TotalProdCost + TotalInvtCost; MODEL MAX Profit = TotalRevenue - TotalCost; SUBJECT TO ProdCapacity[plant, month] -> PCap: SUM(product: Produce / ProdRate) <= ProdDaysAvail; InvtBal[product, month] -> IBal: SUM(plant: Produce) + Inventory[month-1] = Sales + Inventory; MaxInventory[month] -> MaxI: SUM(product: Inventory) <= InvtCapacity; BOUNDS Sales <= Demand; END
Start the MPL application.
Choose File | Open and open the model from the previous session Planning4.mpl.
Choose File | Save As to save it as a new model file Planning5.mpl.
Change the title for the model to reflect that you are working with the Planning5 model:
TITLE Production_Planning5;
In this example, you have four different plant locations. Create a new index which you will call plant. This index will have four elements p1, p2, p3, and p4 to represent each period of the four month planning period. Add the following definition for the plant index to the INDEX section:
INDEX product := (A1, A2, A3); month := (Jan, Feb, Mar, Apr); plant := (p1, p2, p3, p4);
In this session, you are going move the data values for the two-dimensional data vectors to external data files. When working with data vectors that have two dimensions or higher, it is often a good idea to move the data values to an external data file instead of listing all of the numbers directly in the model file. This keeps the data separate from the model, enhances the readability of the model, and makes the data easier to maintain.
The first data vector you want to move to an external data file is the Demand data vector. In the DATA section, use the Cut command from the Edit menu to remove the list of numbers for the Demand vector and then enter the keyword DATAFILE and the filename Demand.dat in its place as follows:
DATA Price[product] := (120.00, 100.00, 115.00); Demand[product, month] := DATAFILE("Demand.dat");
The next step is to create the data file Demand.dat. First, open a new model editor window by choosing New in the File menu. If you used the Edit | Cut command in the previous Step 4 to remove the data values, they are now in the Clipboard and you can use the Edit | Paste command to place the data back into the data file. Otherwise, you can use the demand data table from the problem description in Session 3 to enter the data values into the data file as follows:
! Demand.dat - Demand per month for each product ! ! Demand[product,month]: ! ! Jan Feb Mar Apr ! ---------------------------- 4300, 4200, 6400, 5300, 4500, 5400, 6500, 7200, 5400, 6700, 7800, 8200
The lines that start with exclamation marks are comments used to enhance the readability. The numbers in the data file can be separated by comma or space or both. After you have entered all of the data save the file as Demand.dat in the Tutorial folder.
Two of the data vectors, ProdCost and ProdRate, need to be upgraded to include the plant index. The ProdCost data vector is now defined over two domain indexes, plant and product and will given data values from an external data file. The ProdRate data vector will also be given values from a data file. In the model editor, add the index, plant, to the declaration of both the ProdCost and the ProdRate data vectors, and follow it with the data filenames ProdCost.dat and ProdRate.dat respectively as follows:
ProdCost[plant, product] := DATAFILE("ProdCost.dat"); ProdRate[plant, product] := DATAFILE("ProdRate.dat"); ProdDaysAvail[month] := (23, 20, 23, 22); InvtCost[product] := (3.50, 4.00 3.00); InvtCapacity := 800;
Now open a new editor window by selecting File | New in the menu to enter the data file. Type in the data from the Production Cost table in the problem description as follows:
! ! ProdCost.dat - Cost per item produced ! ! ProdCost[plant, product]: ! ! A1 A2 A3 ! ----------------------- 73.30, 52.90, 65.40, 79.00, 52.00, 66.80, 75.80, 52.10, 50.90, 82.70, 63.30, 53.80
Again, the lines that start with exclamation marks are comments used to enhance the readability. After you have entered all of the data save the file using the name ProdCost.dat.
For the production rate create a new data file called ProdRate.dat using the values from the table in the problem description.
! ! ProdRate.dat - Items produced per day ! ! ProdRate[plant, product]: ! ! A1 A2 A3 ! ------------------ 500, 450, 450, 550, 450, 300, 450, 350, 300, 550, 400, 350
In order to determine how much you want to produce of each product, for each plant, you need to add the plant index to the vector definition of the Produce variable as follows:
VARIABLES Produce[plant, product, month] -> Prod; Inventory[product, month] -> Invt; Sales[product, month] -> Sale;
Since the Produce vector variable now includes the new index, plant, the calculation of the total production cost in the MACROS section needs also to be updated to include the plant index:
MACROS TotalRevenue := SUM(product, month: Price * Sales); TotalProdCost := SUM(plant, product, month: ProdCost * Produce) ; TotalInvtCost := SUM(product, month: InvtCost * Inventory); TotalCost := TotalProdCost + TotalInvtCost;
The objective function itself does not change as you are using the same macros as in the previous session.
The change for the production capacity constraint is very simple. Add the index plant to the production capacity declaration and the rest of the constraint remains the same.
SUBJECT TO ProdCapacity[plant, month] -> PCap: SUM(product: Produce / ProdRate) <= ProdDaysAvail;
You can now produce the products in any of the four plants, therefore, you need to update the inventory balance constraint to include a summation, over all of the plants, of the Produce variable vector.
InvtBal[product, month] -> IBal: SUM(plant: Produce) + Inventory[month-1] = Sales + Inventory;
After you have finished entering the model, you should save it by choosing Save from the File menu.
Since we have added more indexes to the model, the number of variables have increased considerably. Typically, when working with larger models, the model developer wants to include only some of the values in the solution. In our case, to reduce the output, we want to include only the variables that have nonzero solution values. MPL has number of options dialog boxes in the Options menu where you can change the default behavior of the program. One of the dialog boxes is the Solution File Options dialog where you can adjust what is included in the solution file. To change the default to include nonzero values only in the solution file do the following:
From the Options menu choose Solution File to open the Options Dialog Box shown here below:
The Solution File Options Dialog Box
Turn the Nonzero Values Only check box On by clicking on it.
Close the dialog box by pressing the OK button.
After changing the Nonzero Values Only option, the next step is to solve the model by choosing Solve CPLEX from the Run menu. If everything goes well MPL will display the message "Optimal Solution Found". If there is an error message window with a syntax error please check the formulation you entered with the model listing detailed earlier in this session.
As the models you are working with become bigger you tend to look at only certain parts of the solution instead of the whole solution file. This time, instead of listing the whole solution file, we are going to use the model definitions tree window to view only the parts of the solution we are interested in.
The model definitions window allows you to see all of the defined items from the model formulation in a hierarchical tree where each branch corresponds to a section in the model. While in MPL it is normally a good idea to leave the tree window open at all times. MPL will then automatically update its contents every time you solve your model. To look at the model definitions for the Planning5 model choose Model Definitions from the View menu.
The Model Definitions Window for the Planning5 model
From the tree window you can select any of the defined items in the model to look at the actual values for that item. For example, to look at the values for the Produce variable, either double-click on the Produce item in the tree, or select it and then press the View button. This will display a window containing only the values for the Produce variable.
VARIABLE Produce[plant,product,month] : plant product month Activity Reduced Cost ----------------------------------------------------------- p1 A1 Jan 4300.0000 0.0000 p1 A1 Feb 4200.0000 0.0000 p1 A1 Mar 6400.0000 0.0000 p1 A1 Apr 5300.0000 0.0000 p2 A2 Jan 4500.0000 0.0000 p2 A2 Feb 5400.0000 0.0000 p2 A2 Mar 6500.0000 0.0000 p2 A2 Apr 7200.0000 0.0000 p3 A3 Jan 5400.0000 0.0000 p3 A3 Feb 6000.0000 0.0000 p3 A3 Mar 6900.0000 0.0000 p3 A3 Apr 6600.0000 0.0000 p4 A3 Feb 700.0000 0.0000 p4 A3 Mar 900.0000 0.0000 p4 A3 Apr 1600.0000 0.0000 -----------------------------------------------------------
If you look at the activity values for the Produce variable you will see that this time we are fulfilling the demand for all the products since we now have enough capacity. The model decides which plants are used for which products. For example, plant p1 is used to produce product A1, plant p2 is used for product A2, and plants p3 and p4 are used for product A3.
If you go to the tree window again and open a window for the ProdCapacity constraint you will get the following solution values.
CONSTRAINT ProdCapacity[plant,month] : plant month Slack Shadow Price -------------------------------------------------- p1 Jan 14.4000 0.0000 p1 Feb 11.6000 0.0000 p1 Mar 10.2000 0.0000 p1 Apr 11.4000 0.0000 p2 Feb 8.0000 0.0000 p2 Mar 8.5556 0.0000 p2 Apr 6.0000 0.0000 p3 Jan 5.0000 0.0000 p4 Jan 23.0000 0.0000 p4 Feb 18.0000 0.0000 p4 Mar 20.4286 0.0000 p4 Apr 17.4286 0.0000 --------------------------------------------------
There is a great deal of slack for each plant and month in the production capacity constraint. This can be interpreted to mean that we could produce a lot more of the products, but it is not necessary as we are already fulfilling the demand. Since the production capacity constraint uses production days as the unit of measure, the slack values represent how many days per month each plant is idle.